///////////////////////////////////////////////////////////////////////////////
//
// Licensed under the MIT License
//
// Copyright (c) 2010 Chris Colinsky
// All Rights Reserved
//
// version	0.5
// http://code.google.com/p/sugarcookie/
// http://code.google.com/p/sugarcookie/wiki/License
//
////////////////////////////////////////////////////////////////////////////////

package sugarcookie.media {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLLoader;
	
	import sugarcookie.display.SpriteBase;
	import sugarcookie.events.GenericEvent;
	import sugarcookie.file.FileLoader;
	import sugarcookie.media.VideoSourceConstants;
	import sugarcookie.media.config.YTVideoConfiguration;
	import sugarcookie.utils.IObserver;
	import sugarcookie.utils.Observable;

	public class YouTubeVideo extends SpriteBase implements IVideoSource {
		private static const YOUTUBE_API_PREFIX:String = "http://gdata.youtube.com/feeds/api/videos/";
		private static const YOUTUBE_API_VERSION:String = "2";
		private static const YOUTUBE_API_FORMAT:String = "5";
		private static const QUALITY_TO_PLAYER_WIDTH:Object = {
			small: 320,
			medium: 640,
			large: 854,
			hd720: 1280,
			hd1080: 1920
		};
		
		private static const STATE_UNSTARTED:Number = -1;
		private static const STATE_ENDED:Number = 0;
		private static const STATE_PLAYING:Number = 1;
		private static const STATE_PAUSED:Number = 2;
		private static const STATE_BUFFERING:Number = 3;
		private static const STATE_CUED:Number = 5;
		
		private var _src:String;
		private var _config:YTVideoConfiguration;
		private var _availQualityLevels:Array;
		private var _volume:Number;
		private var _state:Number;
		private var _url:String;
		private var _embedCode:String;
		
		private var _isReady:Boolean = false;
		private var _isPlaying:Boolean = false;
		private var _isPaused:Boolean = false;
		private var _isSeeking:Boolean = false;
		private var _isBuffering:Boolean = false;
		
		private var _notifier:Observable;
		private var playerLoader:FileLoader;
		private var youtubePlayer:Object;
		private var youtubeApiLoader:URLLoader;
		
		public function YouTubeVideo(props:Object=null,config:YTVideoConfiguration=null) {
			super(props);
			
			_config = config!=null?config:new YTVideoConfiguration();
			
			init();
		}
		
		//Observable methods
		final public function addObserver(observer:IObserver,notificationType:String="all"):Boolean {
			return _notifier.addObserver(observer,notificationType);
		}
		
		final public function removeObserver(observer:IObserver,notificationType:String="all"):Boolean {
			return _notifier.removeObserver(observer,notificationType);
		}
		
		final public function clearObservers():void {
			_notifier.clearObservers();
		}
		
		final public function countObservers():Number {
			return _notifier.countObservers();
		}
		
		final public function get useLogging():Boolean { return _notifier.useLogging; }
		final public function set useLogging(value:Boolean):void { _notifier.useLogging = value; }

		public function close():void { 
			if (_isReady) {
				youtubePlayer.seekTo(0, false);
				youtubePlayer.stopVideo();
				removeEventListener(Event.ENTER_FRAME,loadProgressHandler);
				_isPaused = false;
				_isPlaying = false;
			}
		}

		public function connect():void {
			playerLoader = new FileLoader(_config.playerURL);
			playerLoader.addEventListener(FileLoader.LOAD_INIT,playerLoadComplete);
			playerLoader.load();
		}
		
		public function get embedCode():String { return _embedCode; }
		
		public function get url():String { return _url; }

		public function get currTime():Number { return (_isReady)?youtubePlayer.getCurrentTime():-1; }

		public function get isPaused():Boolean { return _isPaused; }

		public function get isPlaying():Boolean { return _isPlaying; }

		public function get isStreaming():Boolean { return false; }
		
		public function get state():Number { return _state; }
		
		public function get currentQualityLevel():String {
			if (_isReady) {
				if (youtubePlayer.getPlaybackQuality()==undefined) return _config.qualityLevel;
				else return youtubePlayer.getPlaybackQuality();
			} else {
				return _config.qualityLevel;
			}
		}
		
		public function setNewQualityLevel(value:String):void {
			if (_isReady) youtubePlayer.setPlaybackQuality(value);
			else _config.qualityLevel = value;
		}
		
		public function get availQualityLevels():Array { return (_availQualityLevels)?_availQualityLevels:[]; }
		
		public function get source():String { return _src; }
		public function set source(value:String):void { _src = value; }
		
		public function get videoDur():Number { return (_isReady)?youtubePlayer.getDuration():-1; }
		
		public function get volume():Number { return (_isReady)?youtubePlayer.getVolume()/100:-1; }
		public function set volume(value:Number):void {
			_volume = value*100;
			if (_isReady) youtubePlayer.setVolume(_volume); 
		}

		public function pause():void { 
			if (_isReady) youtubePlayer.pauseVideo();
		}

		public function play(start:int=0, len:int=-1, reset:int=1):void {
			if (_src && !_isPaused && !_isPlaying) {
				youtubePlayer.loadVideoById(_src, start, _config.qualityLevel);
				addEventListener(Event.ENTER_FRAME,loadProgressHandler);
			}
		}

		public function resume():void { 
			if (_isPaused) youtubePlayer.playVideo();
		}

		public function seek(_offset:Number):void { 
			notifyObservers(VideoSourceConstants.STATUS_CHANGE,{code:"NetStream.Seek.Notify"});
			youtubePlayer.seekTo(_offset, false);
		}
		
		private function notifyObservers(type:String,data:Object=null):void {
			_notifier.setChanged();
			_notifier.notifyObservers(type, data);
		}

		private function init():void {
			_notifier = new Observable(this);
		}
		
		private function playerLoadComplete(evt:GenericEvent):void {
			playerLoader.removeEventListener(FileLoader.LOAD_INIT,playerLoadComplete);
			youtubePlayer = playerLoader.file.content;
			addChild(Sprite(youtubePlayer));
			
			youtubePlayer.addEventListener("onReady", playerReady);
			youtubePlayer.addEventListener("onError", playerError);
			youtubePlayer.addEventListener("onStateChange", playerStateChange);
			youtubePlayer.addEventListener("onPlaybackQualityChange", videoPlaybackQualityChange);
		}
		
		private function playerReady(evt:Event):void {
			_isReady = true;
			if (_volume) volume = _volume;
			notifyObservers(VideoSourceConstants.STREAM_READY);
			resizePlayer();
		}
		
		private function playerError(evt:Event):void {
			
		}
		
		private function playerStateChange(evt:Event):void {
			_state = Object(evt).data;
			_log.info("state change to: "+_state);
			switch (_state) {
				case STATE_UNSTARTED:
					break;
				case STATE_ENDED:
					_isPaused = false;
					_isPlaying = false;
					notifyObservers(VideoSourceConstants.PLAYBACK_COMPLETE);
					break;
				case STATE_PLAYING:
					_url = youtubePlayer.getVideoUrl();
					_embedCode = youtubePlayer.getVideoEmbedCode();
					
					if (!_availQualityLevels) {
						_availQualityLevels = youtubePlayer.getAvailableQualityLevels();
						notifyObservers(VideoSourceConstants.QUALITY_LEVELS_AVAILABLE);
					}
					if (_isBuffering) {
						_isBuffering = false;
						notifyObservers(VideoSourceConstants.BUFFERING,{percent:-1, status:_isBuffering});
					}
					
					notifyObservers(VideoSourceConstants.STATUS_CHANGE,{code:"NetStream.Play.Start"});
					
					_isPaused = false;
					_isPlaying = true;
					break;
				case STATE_PAUSED:
					_isPaused = true;
					_isPlaying = false;
					break;
				case STATE_BUFFERING:
					if (!_isBuffering) {
						_isBuffering = true;
						notifyObservers(VideoSourceConstants.BUFFERING,{percent:-1, status:_isBuffering});
					}
					break;
				case STATE_CUED:
					_isPaused = true;
					_isPlaying = false;
					break;
			}
		}
		
		private function loadProgressHandler(evt:Event):void {
			var perc:Number = youtubePlayer.getVideoBytesLoaded()/youtubePlayer.getVideoBytesTotal();
			notifyObservers(VideoSourceConstants.LOAD_PROGRESS,{percent:Number(perc.toPrecision(3))});
			
			if (perc==1) {
				removeEventListener(Event.ENTER_FRAME,loadProgressHandler);
				return;
			}
			
		}
		
		private function videoPlaybackQualityChange(evt:Event):void {
			notifyObservers(VideoSourceConstants.QUALITY_CHANGE,{quality:currentQualityLevel});
		}
		
		private function resizePlayer():void {
			var newWidth:Number;
			if (_config.width!=0) newWidth = _config.width;
			else newWidth = QUALITY_TO_PLAYER_WIDTH[_config.qualityLevel] || 640;
			
			var newHeight:Number;
			if (_config.height!=0) newHeight = _config.height;
			else newHeight = newWidth * 9 / 16;
			
			youtubePlayer.setSize(newWidth, newHeight);
		}
	}
}